import 'dart:math' as math;
import 'dart:ui';
import 'dart:ui' as ui;

import 'package:flutter/material.dart';
import 'package:path_drawing/path_drawing.dart';

import 'hc_gesture_detector.dart';

class HcPainter extends CustomPainter {
  Model paintModel;
  ui.Image _imageFrame;

  HcPainter(this.paintModel, this._imageFrame) : super(repaint: paintModel);

  @override
  void paint(Canvas canvas, Size size) {
//    canvas.drawColor(Colors.lightGreen[300], BlendMode.color);
    canvas.scale(paintModel.scale);

    Paint paint = new Paint()
      ..color = Colors.red
      ..strokeCap = StrokeCap.round
      ..strokeWidth = 5.0;

    if (_imageFrame != null) {
      canvas.drawImage(_imageFrame, paintModel.imgNode.displayPos, Paint());
    }

    canvas.drawCircle(paintModel.reference.displayPos, 4 / paintModel.scale,
        Paint()..color = Colors.green);

    for (int i = 0; i < paintModel.line.length - 1; i++) {
      if (paintModel.line[i] != null && paintModel.line[i + 1] != null)
        canvas.drawLine(paintModel.line[i].displayPos,
            paintModel.line[i + 1].displayPos, paint);
    }

    if (paintModel.refLine.length > 1) {
      // Paint paint1 = new Paint()
      //   ..color = Colors.red
      //   ..strokeCap = StrokeCap.round
      //   ..strokeWidth = 5.0;
      // for (int i = 0; i < paintModel.refLine.length - 1; i++) {
      //   if (paintModel.refLine[i] != null && paintModel.refLine[i + 1] != null)
      //     canvas.drawLine(paintModel.refLine[i].displayPos,
      //         paintModel.refLine[i + 1].displayPos, paint1);
      // }

      var last2 = paintModel.refLine[paintModel.refLine.length - 2];
      var last1 = paintModel.refLine[paintModel.refLine.length - 1];

      final Path p = Path()
        ..moveTo(last2.displayPos.dx, last2.displayPos.dy)
        ..lineTo(last1.displayPos.dx, last1.displayPos.dy);
      final Paint black = Paint()
        ..color = Colors.black
        ..strokeWidth = 1.0
        ..style = PaintingStyle.stroke;
      canvas.drawPath(
          dashPath(
            p,
            dashArray: CircularIntervalList<double>(
              <double>[5.0, 2.5],
            ),
          ),
          black);
    }

    if (paintModel.agv != null) {
      canvas.drawCircle(paintModel.agv.displayPos, 4 / paintModel.scale,
          Paint()..color = Colors.red);

      // 1-2 段落构造器并添加文本信息
      ParagraphBuilder _pb = ParagraphBuilder(
          ParagraphStyle(fontWeight: FontWeight.normal, fontSize: 17))
        ..pushStyle(ui.TextStyle(color: Colors.blue))
        ..addText(
            // (paintModel.center.pos / paintModel.scale - paintModel.zero.pos)
            //         .toString() +
            //     '---' +
            (paintModel.center.displayPos / paintModel.scale -
                    paintModel.zero.displayPos)
                .toString());
// 3 设置段落容器宽度
      ParagraphConstraints pc = ParagraphConstraints(width: size.width - 100);
// 4 计算文本位置及尺寸
      Paragraph paragraph = _pb.build()..layout(pc);
// 5 文本绘制
      canvas.drawParagraph(paragraph, Offset(0.0, 0.0));
    }

//缩放指示
    if (paintModel.shouldDrawControlPoint) {
      canvas.drawCircle(paintModel.focalPoint / paintModel.scale,
          4 / paintModel.scale, Paint());
      canvas.drawCircle(paintModel.focalPoint / paintModel.scale,
          2 / paintModel.scale, Paint()..color = Colors.red);
    }

    // 中心点
    canvas.drawCircle(paintModel.center.displayPos / paintModel.scale,
        4 / paintModel.scale, Paint()..color = Colors.red);
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) {
    return true;
  }
}

class Model extends ChangeNotifier {
  double baseScale;
  double _scale = 1.0;
  double _rotate = 0;

  /**
   * 原始焦点
   */
  Offset _originFocalPoint = Offset(0, 0);
  bool _isMove = false;
  bool _isScale = false;

  /**
   * 当前焦点
   */
  Offset _focalPoint = Offset(0, 0);

  /**
   * 开始点
   */
  Offset _startPoint = Offset(0, 0);
  Offset moveLocalPos = Offset(0, 0);
  bool shouldDrawControlPoint = false;
  List<Node> _line;
  bool isEdit = true;

  Node _zero = Node(Offset(0, 0));

  /**
   * 参考点（0，0）
   */
  Node get zero => _zero;

  set zero(Node value) {
    _zero = value;
  }

  Node _center = Node(Offset(0, 0));

  Node get center => _center;

  set center(Node value) {
    _center = value;
  }

  List<Node> _refLine = <Node>[];

  ///参考线
  List<Node> get refLine => _refLine;

  set refLine(List<Node> value) {
    _refLine = value;
  }

  Node _reference = Node(Offset(300, 300));

  /**
   * 参考点（10，10）
   */
  Node get reference => _reference;

  set reference(Node value) {
    _reference = value;
  }

  Node _agv = Node(Offset(0, 0));

  Node get agv => _agv;

  set agv(Node value) {
    _agv = value;
  }

  setAgv(Offset value) {
    Node n = Node(value);
    n.pos += zero.pos;
    n.displayPos += zero.displayPos;
    agv = n;
    notifyListeners();
    moveTo(n.displayPos);
  }

  Node _imgNode;

  Node get imgNode => _imgNode;

  set imgNode(Node value) {
    _imgNode = value;
  }

  List<Node> get line => _line;

  set line(List<Node> value) {
    _line = value;
  }

  get scale => baseScale * _scale;

  get rotate => _rotate;

  get focalPoint => _focalPoint;

  get originFocalPoint => _originFocalPoint;

  Node currentNode;

  Size containerSize;
  Size imgSize;

  Model(this.baseScale, this._rotate, this._line, this._imgNode) {}

  static createDefault() {
    List<Node> _points = <Node>[Node(Offset(90, 120)), Node(Offset(169, 178))];
    Node _imgNode = new Node(Offset.zero);
    return Model(1.8, 0.0, _points, _imgNode);
  }

  void setSize(Size _size, Size _imgSize) {
    this.containerSize = _size;
    this.imgSize = _imgSize;
    center = Node(
        Offset(this.containerSize.width / 2, this.containerSize.height / 2));
  }

  void setPoint() {
    var c = center.displayPos / scale - zero.displayPos;
    if (c.dy < 0 ||
        c.dx < 0 ||
        c.dy > this.imgSize.height ||
        c.dx > this.imgSize.width) {
      return;
    }
    var n = Node((center.displayPos / scale - zero.displayPos));
    n.pos += zero.pos;
    n.displayPos += zero.displayPos;
    line.add(n);

    changeRefLine();
    notifyListeners();
  }

  void changeRefLine() {
    if (refLine.isNotEmpty) {
      refLine.clear();
    }
    if (line.length > 1) {
      var last2 = line[line.length - 2];
      var last1 = line[line.length - 1];
      refLine.add(Node(last2.displayPos));
      refLine.add(Node(last1.displayPos));

      var d =
          Node(getDirectionOffset(last1.pos - zero.pos, last2.pos - zero.pos));
      d.pos += zero.pos;
      d.displayPos += zero.displayPos;
      var r =
          Node(getDirectionOffset(last1.pos - zero.pos, last2.pos - zero.pos));
      r.pos += zero.pos;
      r.displayPos += zero.displayPos;
      this.reference = r;
      refLine.add(d);
    }
  }

  getDirectionOffset(Offset last1, Offset last2) {
    if (last1 == last2) {
      return last1;
    }
    //先判断方向
    var xd = last2.dx - last1.dx;
    //xd>0,向左，xd<0向右
    var yd = last2.dy - last1.dy;
    //yd>0,向上，yd<0向下
    if (xd == 0) {
      if (yd > 0) {
        //top
        return Offset(last1.dx, 0);
      } else if (yd < 0) {
        //bottom
        return Offset(last1.dx, imgSize.height);
      }
    } else if (xd > 0) {
      //x,向左
      if (yd == 0) {
        return Offset(0, last1.dy);
      } else if (yd > 0) {
        //top
        var y = (last2.dy - last1.dy).abs();
        var x = (last2.dx - last1.dx).abs();

        try {
          var tY = 0.0;
          var tX = 0.0;
          tY = y * last2.dx / x;
          if (tY > last2.dy) {
            tX = last2.dy * x / y;
            return Offset(last2.dx - tX, 0);
          } else {
            return Offset(0, last2.dy - tY);
          }
        } catch (e) {
          print(e);
        }
      } else if (yd < 0) {
        //bottom
        var y = (last2.dy - last1.dy).abs();
        var x = (last2.dx - last1.dx).abs();

        try {
          var tY = 0.0;
          var tX = 0.0;
          tY = last2.dy + y * last2.dx / x;
          if (tY > imgSize.height) {
            tX = x * (imgSize.height - last2.dy) / y;
            return Offset(last2.dx - tX, imgSize.height);
          } else {
            return Offset(0, tY);
          }
        } catch (e) {
          print(e);
        }
      }
    } else if (xd < 0) {
      //x,向右
      if (yd == 0) {
        return Offset(this.imgSize.width, last1.dy);
      } else if (yd > 0) {
        //top
        var y = (last2.dy - last1.dy).abs();
        var x = (last2.dx - last1.dx).abs();

        try {
          var tY = 0.0;
          var tX = 0.0;
          tY = y * (this.imgSize.width - last2.dx) / x;
          if (tY > last2.dy) {
            tX = last2.dy * x / y;
            return Offset(last2.dx + tX, 0);
          } else {
            return Offset(this.imgSize.width, last2.dy - tY);
          }
        } catch (e) {
          print(e);
        }
      } else if (yd < 0) {
        //bottom
        var y = (last2.dy - last1.dy).abs();
        var x = (last2.dx - last1.dx).abs();

        try {
          var tY = 0.0;
          var tX = 0.0;
          tY = last2.dy + y * (this.imgSize.width - last2.dx) / x;
          if (tY > imgSize.height) {
            tX = x * (imgSize.height - last2.dy) / y;
            return Offset(last2.dx + tX, imgSize.height);
          } else {
            return Offset(imgSize.width, tY);
          }
        } catch (e) {
          print(e);
        }
      }
    }

    //top
    var topDx = last1.dx * last1.dx / last2.dx;
    var top = Offset(topDx, 0);
  }

  void changeEdit() {
    isEdit = !isEdit;
  }

  void updateScaleRotate(
      double newScale, Offset changedFocusPoint, double newRotation) {
    _scale = newScale;
    _rotate = 0 - newRotation;

    _startPoint = _originFocalPoint - _focalPoint / scale;

    line.forEach((e) {
      e.displayPos =
          rotateOffset(_originFocalPoint, e.pos, _rotate) - _startPoint;
    });
    refLine.forEach((e) {
      e.displayPos =
          rotateOffset(_originFocalPoint, e.pos, _rotate) - _startPoint;
    });

    imgNode.displayPos =
        rotateOffset(_originFocalPoint, imgNode.pos, _rotate) - _startPoint;
    reference.displayPos =
        rotateOffset(_originFocalPoint, reference.pos, _rotate) - _startPoint;
    zero.displayPos =
        rotateOffset(_originFocalPoint, zero.pos, _rotate) - _startPoint;

    agv.displayPos =
        rotateOffset(_originFocalPoint, agv.pos, _rotate) - _startPoint;

    notifyListeners();
  }

  Offset rotateOffset(Offset centerPos, Offset currentPos, double angle) {
    double distance = (currentPos - centerPos).distance;
    double finalAngle =
        math.atan2(currentPos.dx - centerPos.dx, currentPos.dy - centerPos.dy) -
            angle;

    return Offset(centerPos.dx + distance * math.sin(finalAngle),
        centerPos.dy + distance * math.cos(finalAngle));
  }

  void staging() {
    this.baseScale *= this._scale;
    this._scale = 1.0;
    updateModel(_originFocalPoint, _focalPoint, _rotate);
    this._rotate = 0.0;
    shouldDrawControlPoint = false;
    this._isScale = false;
  }

  void updateModel(Offset oFocalPoint, Offset focalPoint, double rotateValue) {
    line.forEach((e) {
      e.pos = e.displayPos + _startPoint;
    });
    refLine.forEach((e) {
      e.pos = e.displayPos + _startPoint;
    });
    imgNode.pos = imgNode.displayPos + _startPoint;
    reference.pos = reference.displayPos + _startPoint;
    zero.pos = zero.displayPos + _startPoint;
    if (agv != null) {
      agv.pos = agv.displayPos + _startPoint;
    }
  }

  void startMoving(Offset position) {
    this._isMove = true;
  }

  void moving(MoveEvent event) {
    var delta = event.delta / scale;
    moveLocalPos = event.localPos;
    if (!isEdit) {
      line.forEach((e) {
        e.pos += delta;
        e.displayPos += delta;
      });
      refLine.forEach((e) {
        e.pos += delta;
        e.displayPos += delta;
      });
      imgNode.pos += delta;
      imgNode.displayPos += delta;
      reference.pos += delta;
      reference.displayPos += delta;
      zero.pos += delta;
      zero.displayPos += delta;
      agv.pos += delta;
      agv.displayPos += delta;
    } else {
      _originFocalPoint =
          (event.localPos - _focalPoint) / scale + _originFocalPoint;
      _focalPoint = event.localPos;
      Node node = new Node(_originFocalPoint);
      node.displayPos = event.localPos / scale;
      print(node.pos);
      print(node.displayPos);
      line.add(node);
    }
    notifyListeners();
  }

  void endMoving() {
    currentNode = null;
    this._isMove = false;
  }

  void startScaleRotate(Offset initialFocusPoint) {
    _originFocalPoint =
        (initialFocusPoint - _focalPoint) / scale + _originFocalPoint;
    _focalPoint = initialFocusPoint;
    shouldDrawControlPoint = true;
    this._isScale = true;
  }

  void updateDynamicNode(Offset nd) {
//    print('-----');
    Node n = Node(nd);
    n.pos += zero.pos;
    n.displayPos += zero.displayPos;

    if (line.length > 0) {
      var first = line.first;
//      print('0000000000000');
//      print(first.displayPos);
//      print(first.pos);
//      print('0000000000000');
    }
//    print(nd);
//    print(n.displayPos);
//    print(n.pos);
//    print(zero.displayPos);
//    print(zero.pos);
//    print(reference.displayPos);
//    print(reference.pos);
//    print(scale);
//    print('============');
//    print('rrrrrrrrrrrrrr');
    this.agv = n;
//    print(this.agv.displayPos);
    notifyListeners();
//    print(this.agv.displayPos);
//    print('iiiiiiiiiiii');
  }

  void moveTo(Offset center) {
    var delta = Offset(0, 0);
    if (scale != 1.0) {
      delta = Offset(-(center.dx - containerSize.width / scale / 2),
          -(center.dy - containerSize.height / scale / 2));
    } else {
      delta = Offset(-(center.dx - containerSize.width / 2),
          -(center.dy - containerSize.height / 2));
    }
    line.forEach((e) {
      e.pos += delta;
      e.displayPos += delta;
    });
    refLine.forEach((e) {
      e.pos += delta;
      e.displayPos += delta;
    });
    imgNode.pos += delta;
    imgNode.displayPos += delta;
    reference.pos += delta;
    reference.displayPos += delta;
    zero.pos += delta;
    zero.displayPos += delta;
    agv.pos += delta;
    agv.displayPos += delta;
    notifyListeners();
  }
}

class Node {
  Offset pos;
  Offset displayPos;

  Node(this.pos) {
    displayPos = this.pos;
  }
}
